# coding:utf-8
# writingtime: 2022-7-29
# coding:utf-8
# writingtime: 2022-6-28
# author: wanjun
import os
import datetime

from Utilities.AutoGetOperator.selectPackage import get_func
from Utilities.Plot.SimplePlot import SimpleClass
from Utilities.AutoGetOperator.GetNmaeFunc.getOperatorClass import GetOperatorClass

class SingleOperatorTestRandomData:
    def __init__(self, operator=None, getExpertWeight=None, getAttributeWeight=None,
                 scoreFunction=None, get_data=None, map2D=True, mapRadar=False, mapDeviation=False,
                 orderDeviation=None, imgShow=False, imgSaving=False,
                 q=3, x=2, a=2, b=2, n=4, m=5, *wast1, **waste2):
        '''
         function: 测试单个算子
        :param operator: 算子的类方法
        :param getExpertWeight: 计算专家权重的类方法
        :param getAttributeWeight: 计算属性权重的类方法
        :param scoreFunction: 得分函数的类方法
        :param get_data: 实例化后的类中的方法
        :param map2D: 是否绘制二维单图
        :param mapRadar:  是否绘制雷达图
        :param mapDeviation: 是否绘制偏差图
        :param orderDeviation: 偏差图中只选取4个方案，默认为1,2,3,4
        :param imgShow:  是否展示图片
        :param imgSaving:  是否保存图片
        :param q: 参数q
        :param x: 参数x
        :param a: 参数a
        :param b: 参数b
        :param wast1: 接受多余参数
        :param waste2: 接受多余参数
        '''

        # 对算子的类进行初始化
        if operator is None:
            self.operator = get_func(r'Operators\OperationOperators\Algebraic.py', 'WA')
        else:
            self.operator = operator

        # 计算专家权重的类进行初始化
        if getExpertWeight is None:
            self.getExpertWeight = get_func(r'Utilities\GetWeight\getExpertWeight\GetSatisfactionWeight.py',
                                            'getSatisfactionWeight')
        else:
            self.getExpertWeight = getExpertWeight

        # 计算属性权重的类初始化
        if getAttributeWeight is None:
            self.getAttributeWeight = get_func(r'DecisionMethod\MABAC.py', 'MABAC')
        else:
            self.getAttributeWeight = getAttributeWeight

        # 得分函数的类初始化
        if scoreFunction is None:
            self.getScore = get_func(r'ScoreFunction\getS.py', 'getS')
        else:
            self.getScore = scoreFunction

        # 获取随机数据方法的初始化
        if get_data is None:
            self.get_dataGroup = get_func(r'Data/randomData.py',
                                          'RandomData')().get_Gaussian_random
        else:
            self.get_dataGroup = get_data

        # 对偏差图展示的方案进行初始化
        if orderDeviation is None:
            self.orderDeviation = [1, 2, 3, 4]
        else:
            self.orderDeviation = orderDeviation
        self.data_group = []  # 决策群
        self.expert_weight = []  # 专家权重
        self.attribute_weight = []  # 属性权重
        self.q = q  # q的值
        self.x = x  # 一个参数x
        self.a = a  # 两个参数时的a
        self.b = b  # 两个参数时的b
        self.map2D = map2D  # 是否绘制二维单图
        self.mapRadar = mapRadar  # 是否绘制雷达图
        self.mapDeviation = mapDeviation  # 是否绘制偏差图
        self.imgShow = imgShow  # 显示图片
        self.imgSaving = imgSaving  # 保存图片
        self.n = n  # 设置决策群最小数
        self.m = m  # 设置决策群的最大值

    def set_dataGroup(self, data_group):
        '''
        function: 设置决策数据群
        :param data_group: 决策群
        :return:
        '''
        self.data_group = data_group

    def setExpertWeight(self, expert_weight):
        '''
        function: 设置专家权重
        :param expert_weight:专家权重
        :return:
        '''
        self.expert_weight = expert_weight

    def setAttributeWeight(self, attribute_weight):
        '''
        function: 设置属性权重
        :param attribute_weight: 属性权重
        :return:
        '''
        self.attribute_weight = attribute_weight

    def set_q(self, q):
        '''
        function: 对q的值进行修改
        :param q:
        :return:
        '''
        self.q = q

    def set_x(self, x):
        '''
        function: 对x的值进行修改
        :param x:
        :return:
        '''
        self.x = x

    def set_a(self, a):
        '''
        function: 对a的值进行修改
        :param a:
        :return:
        '''
        self.a = a

    def set_b(self, b):
        '''
        function: 对b的值进行修改
        :param b:
        :return:
        '''
        self.b = b

    def matrixReverse(self, matrix):
        '''
        function: 功能函数
        :param matrix: 矩阵
        :return: 行列交换的矩阵
        '''
        newMatrix = []
        for x in range(len(matrix[0])):
            temp = []
            for y in range(len(matrix)):
                temp.append(matrix[y][x])
            newMatrix.append(temp)

        return newMatrix

    def arrge(self):
        '''
        function: 对决策群进行集结
        :return:
        '''

        aggre_matrix_1 = []  # 存放运算结果
        '''集结矩阵'''
        for i in range(len(self.data_group[0])):
            li_1 = []
            for j in range(len(self.data_group[0][0])):
                li_1.append(
                    self.operator([self.data_group[k][i][j] for k in range(len(self.data_group))], self.expert_weight,
                                  q=self.q, x=self.x, a=self.a, b=self.b).getResult())
            aggre_matrix_1.append(li_1)
        # 属性权重,对于属性进行分析，因此需要翻转
        reverseMatrix = self.matrixReverse(aggre_matrix_1)
        self.attr_weight = self.getAttributeWeight(reverseMatrix,
                                                   [1 / len(reverseMatrix[0]) for i in range(len(reverseMatrix[0]))],
                                                   self.q).getResult()
        # 最终结果
        fianl_vlaue = [self.operator(i, self.attr_weight, q=self.q, x=self.x,
                                     a=self.a, b=self.b).getResult() for i in aggre_matrix_1]

        return fianl_vlaue

    def saveData(self, fname='1.txt', data_group=None, expertWeight=None, atttributeWeight=None, q_range=None):
        '''
        function: 存储数据
        :param data_group: 决策群
        :param expertWeight: 专家权重
        :param atttributeWeight: 属性权重
        :return:
        '''

        # 写入data数据
        try:
            # data_file = open(fname, "w")
            # data_file.write("q:\n" + str(q_range) + "\n" +
            #                 "data set:\n" + str(data_group) + "\n" +
            #                 "expert weight:\n" + str(expertWeight) + "\n"
            #                                                          "attribute weight:\n" + str(
            #     atttributeWeight) + "\n")
            print(fname + " is saved")
            # data_file.close()
        except:
            print(fname + " fail")
        # 存储数据

    def q_analyze(self, q_min, q_max):
        '''
        function: q的敏感度分析
        :param q_min: q的下限，不能小于决策数据中最的最小q值
        :param q_max: q的上限
        :return:
        '''

        # 创建文件夹
        root_path = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))
        result_path=os.path.join(root_path, r'Result\singleOperatorTestRandomData')
        result_path=os.path.join(result_path, self.get_dataGroup.__name__)
        path = os.path.join(result_path, datetime.datetime.now().strftime('%Y-%m-%d'))
        fpath = os.path.join(path, r'img')
        tpath = os.path.join(path, r'data')
        try:
            os.makedirs(fpath)  # 图片存储地址
            os.makedirs(tpath)  # 数据存储地址
        except:
            print('folder already exists')

        n = self.n
        m = self.m
        for plan_n in range(n, m):
            for expe_n in range(n, m):
                for attr_n in range(n, m):
                    # 随机生成决策群 [expe_n, plan_n, attr_n]
                    data = self.get_dataGroup(size=[expe_n, plan_n, attr_n])
                    # 更新决策群
                    self.set_dataGroup(data)

                    value_list = []
                    for i in range(q_min, q_max + 1):
                        self.set_q(i)
                        # 更新专家权重
                        self.expert_weight = self.getExpertWeight(self.data_group, q=self.q).getResult()
                        temp = self.arrge()
                        # print(len(temp),len(temp[0]))
                        value_list.append([self.getScore(temp[i], self.q).getScore() for i in range(plan_n)])

                    # 数据保存路径
                    tname = os.path.join(tpath, str(plan_n) + '_' + str(expe_n) + '_' + str(attr_n) + '.txt')
                    # 保存数据
                    self.saveData(tname, self.data_group, self.expert_weight, self.attr_weight,
                                  [q_min + i for i in range(q_max - q_min + 1)])

                    # 二维单图
                    if self.map2D:
                        # 将结果转置
                        value_list = self.matrixReverse(value_list)
                        fname = os.path.join(fpath,
                                             'map2D' + str(plan_n) + '_' + str(expe_n) + '_' + str(attr_n) + '.png')
                        SimpleClass().qSensitivity(value_list, [q_min + i for i in range(q_max - q_min + 1)],
                                                   title=self.operator.__name__,
                                                   filename=fname, imgSaving=self.imgSaving, imgShow=self.imgShow)

                    # 雷达图
                    if self.mapRadar:
                        fname = os.path.join(fpath,
                                             'mapRadar' + str(plan_n) + '_' + str(expe_n) + '_' + str(attr_n) + '.png')
                        if q_max - q_min + 1 <= 10:
                            SimpleClass().radarPlot(value_list,
                                                    ['q=%s' % str(q_min + i) for i in range(q_max - q_min + 1)],
                                                    title=self.operator.__name__, filename=fname,
                                                    imgSaving=self.imgSaving, imgShow=self.imgShow)
                        else:
                            li = []
                            name_list = []
                            # 选取从q_min开始的8个q值
                            for i in range(q_min, q_min + 8):
                                li.append(value_list[i - q_min])
                                name_list.append('q={}'.format(i))
                            # 以10为间隔取数据
                            n = int((q_max - q_min + 1) / 10)
                            for i in range(1, n + 1):
                                li.append(value_list[i * 10 + 4])
                                name_list.append('q={}'.format(i * 10))
                            li = self.matrixReverse(li)
                            SimpleClass().radarPlot(li, name_list, title=self.operator.__name__, filename=fname,
                                                    imgSaving=self.imgSaving, imgShow=self.imgShow)
                    # 偏差图
                    if self.mapDeviation:
                        fname = os.path.join(fpath,
                                             'mapDeviation' + str(plan_n) + '_' + str(expe_n) + '_' + str(
                                                 attr_n) + '.png')
                        SimpleClass().deviationPlot(value_list, planOrder=self.orderDeviation, planSum=plan_n,
                                                    filename=fname, imgShow=self.imgShow, imgSaving=self.imgSaving)

if __name__ == '__main__':

    op_list = GetOperatorClass('Einstein').getOperatorPath()
    EinsteinWA=get_func(op_list['Einstein'],'EinsteinWA')
    # 展示简单二维图将map2D设置为True，其他展示的图设置为False，example中只展示图，不保存任何数据
    SingleOperatorTestRandomData(operator=EinsteinWA, map2D=True,
        mapRadar=False, mapDeviation=False,imgShow=True, imgSaving=False).q_analyze(2, 10)
    # 展示雷达图将mapRadar设置为True，其他展示的图设置为False，example中只展示图，不保存任何数据
    # SingleOperatorTestRandomData(operator=EinsteinWA, map2D=False,
    #     mapRadar=True, mapDeviation=False,imgShow=True, imgSaving=False).q_analyze(2, 10)
    # 展示偏差图将mapDeviation设置为True，其他展示的图设置为False，example中只展示图，不保存任何数据
    # SingleOperatorTestRandomData(operator=EinsteinWA, map2D=False,
    #     mapRadar=False, mapDeviation=True,imgShow=True, imgSaving=False).q_analyze(2, 10)

